#!/usr/bin/python
# -*- coding: utf-8 -*-


# cmap2owl -- Helper application to convert from concept maps to OWL ontologies
# Copyright (c) 2008-2013  Rodrigo Rizzi Starr
#  
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#  
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


'''
A class that draws cmaps

@author: Rodrigo Rizzi Starr
@copyright: Copyright © 2008-2013 Rodrigo Rizzi Starr
@license: MIT License
@contact: rodrigo.starr@gmail.com
'''



from __future__ import division
import sys

# GTK+ imports
import pygtk
if not sys.platform == 'win32':
    pygtk.require('2.0')
try:
    import gobject
    import gtk
    import gtk.gdk
except:
    log.error("gtk or gobject or gtk.gdk module not found!")
    sys.exit(1)

# Project imports
from log import log
from canvas import Canvas
from ifcontrollers import PanController

log.debug("Loaded...")


class CMapDraw(Canvas):
    """
      Has special utilities to draw a cmap on the screen and highlight some of
      its nodes.
    """
    
    # Draw in response to an expose-event
    __gsignals__ = { "selection-changed" : (gobject.SIGNAL_RUN_LAST,
                                            gobject.TYPE_NONE,
                                            (gobject.TYPE_PYOBJECT,)),
                     "show-actions-menu" : (gobject.SIGNAL_RUN_LAST,
                                            gobject.TYPE_NONE,
                                            (gobject.TYPE_PYOBJECT,)),
                     }
    
    def __init__(self, widget, graph, owner):
        Canvas.__init__(self)
        
        self.widget = widget
        self.widget.add_with_viewport(self)
        
        pc = PanController(panButton=2)
        pc.connect(canvas=self,
                   scrolledWindow = self.widget)
        
        self.owner = owner
        self.graph = graph
        self.color = (0, 0, 0)
        self.alpha = 0
        
        # The limits of the cmap
        self.width  = 0
        self.height = 0
        self.calculateCMapSize()
        self.set_size_request(self.width, self.height)
        
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK) 
        self.connect("button-press-event", self.handleButtonPress)
        
        # Elements that are part of the graph
        self.elements = []
        for el in self.graph.getElements():
            el = el.graphicalProperties
            el.canvas = self
            self.elements.append(el)
        
        # Elements that are currently selected and highlighted
        self.highlighted = []
        self.selected = []
        
    def calculateCMapSize(self):
        """Finds the cmap bounding box and transforms every element to this new
           bbox"""
        
        min_x = 1E200
        min_y = 1E200
        max_x = 0
        max_y = 0
        
        for el in self.graph.getElements():
            el = el.graphicalProperties
            min_x = min_x if min_x <= el.x else el.x
            min_y = min_y if min_y <= el.y else el.y
            max_x = max_x if max_x >= el.x + el.width else el.x + el.width
            max_y = max_y if max_y >= el.y + el.height else el.y + el.height
        
        # Now we scan the connections to find the maxima and minima
        for el in self.graph.getConnections():
            el = el.graphicalProperties
            for x, y in el.controlPoints:
                min_x = min_x if min_x <= x else x - 5
                min_y = min_y if min_y <= y else y - 5
                max_x = max_x if max_x >= x else x + 5
                max_y = max_y if max_y >= y else y + 5
        
        self.width  = max_x - min_x + 20
        self.height = max_y - min_y + 20
        min_x  = min_x - 10
        min_y  = min_y - 10

        
        # Now corrects every graphical element for the new dimensions
        for el in self.graph.getElements():
            el = el.graphicalProperties
            el.x -= min_x
            el.y -= min_y
        
        for el in self.graph.getConnections():
            el = el.graphicalProperties
            newCPs = []
            for x, y in el.controlPoints:
                newCPs.append( (x - min_x, y - min_y) )
            el.controlPoints = newCPs
    
    def highlight(self, elements):
        "Highlight the following elements"
        self.highlighted = elements
        self.redraw_canvas()

    def handleButtonPress(self, widget, event):
        "Checks which mouse button was pressed and handle accordingly"
        if event.type == gtk.gdk.BUTTON_PRESS and \
           (event.button == 1 or event.button == 3):
            self.selected = []
            for el in self.elements:
                if el.contains(event.x, event.y):
                    el.focus = True
                    self.selected.append(el.id)
                else:
                    el.focus = False
                    
            self.emit("selection-changed", self.selected)
            
            # Now we show a special action menu in the case of right-click
            if event.button == 3 and len(self.selected) > 0:
                self.emit("show-actions-menu", event)
            
            return True
        else:
            return False
      
    def draw(self):
        "Draws everything"
      
        self.drawBackground()

        for node in self.elements:
            node.draw()
        
        if len(self.highlighted) != 0:
            for elId in self.highlighted.toHighlight:
                self.drawFocusOn(self.graph.getElementById(elId))
    
    def drawFocusOn(self, concept):
        "Focus an element of the graph"

        if concept == None:
            return
        
        el = concept.graphicalProperties
        cr = self.cr
        
        x = el.x
        y = el.y
        width = el.width
        height = el.height
        
        delta = 7 # 7 pixels sounds good
        
        cr.set_source_rgba(1.0, 0.55686, 0.5647058823, .5)
        self.drawRoundBox(x-delta,
                          y-delta,
                          width+2*delta,
                          height+2*delta)
        cr.fill()
        
    def getHighlightedCG(self):
        """Return a tuple (x, y) with the center of the box bounding the
           highlighted elements
        """
        
        if len(self.highlighted) != 0:
            min_x = self.width 
            min_y = self.height
            max_x = 0
            max_y = 0
            
            for elId in self.highlighted.toHighlight:
                element = self.graph.getElementById(elId)
                element = element.graphicalProperties

                min_x = min(min_x, element.x)
                min_y = min(min_y, element.y)
                max_x = max(max_x, element.x + element.width)
                max_y = max(max_y, element.y + element.height)
                
            x = (max_x - min_x)/2 + min_x 
            y = (max_y - min_y)/2 + min_y
            
            return (x, y)
            
        else:
            return (-1, -1)
